/*
 *  $Id$

    Copyright (C) 2008  Nathaniel Waisbrot

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package termint.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import termint.brains.PriceWatcher;
import termint.util.Pair;

/**
 * @author Nathaniel Waisbrot
 *
 */
public class PriceModel implements PriceWatcher.Listener, Serializable {
	public static interface ChangeListener {
		public void tableChanged(int rowCount);
	}
	final static Logger log = LoggerFactory.getLogger(PriceModel.class);
	private static class ItemPrice implements Comparable<ItemPrice> {
		private boolean fixedPrice = false;
		private Pair<Integer,Integer> prices;
		private int price = -1;
		public ItemPrice(Pair<Integer,Integer> pricePair) {
			prices = pricePair;
		}
		/** returns true if the price changed */
		public boolean addPrice(Pair<Integer,Integer> pricePair) {
			if (fixedPrice)
				return false; // TODO: do we want to confirm price is OK?
			if (prices.equals(pricePair))
				return false;
			
			if (closeEnough(prices.a, pricePair))
				price = prices.a;
			else if (closeEnough(prices.b, pricePair))
				price = prices.b;
			else {
				log.error("Got conflicting prices: {} and {}", prices, pricePair);
				return false;
			}
			fixedPrice = true;
			return true;
		}
		private boolean closeEnough(int a, Pair<Integer,Integer> b) {
			return closeEnough(a, b.a) || closeEnough(a, b.b);
		}
		private boolean closeEnough(int a, int b) {
			return Math.abs(a-b) < 5;
		}
		private int onePrice() {
			if (fixedPrice)
				return price;
			else
				return prices.a;
		}
		@Override
		public String toString() {
			if (fixedPrice)
				return Integer.toString(price);
			else
				return prices.toString();
		}
		@Override
		public int compareTo(ItemPrice o) {
			return Integer.compare(onePrice(), o.onePrice());
		}
	}
	private final Map<String,ItemPrice> priceMap = new HashMap<>();
	private final List<String> ordering = new ArrayList<>();
	private final List<ChangeListener> listeners = new ArrayList<>();
	@Override
	public void price(String name, Pair<Integer, Integer> prices) {
		ItemPrice price = priceMap.get(name);
		if (price == null) {
			price = new ItemPrice(prices);
			priceMap.put(name, price);
			ordering.add(name);
			notifyListeners();
		} else {
			if (price.addPrice(prices))
				notifyListeners();
		}
	}
	public void addListener(ChangeListener l) {
		listeners.add(l);
	}
	private void notifyListeners() {
		for (ChangeListener l : listeners) {
			l.tableChanged(ordering.size());
		}
	}
	public void sortByName() {
		Collections.sort(ordering, new Comparator<String>() {
			@Override public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}});
	}
	public void sortByPrice() {
		Collections.sort(ordering, new Comparator<String>() {
			@Override public int compare(String o1, String o2) {
				ItemPrice i1 = priceMap.get(o1);
				ItemPrice i2 = priceMap.get(o2);
				return i1.compareTo(i2);
			}});
	}
	public String[] dataForRow(int i) {
		String name = ordering.get(i);
		String price = priceMap.get(name).toString();
		return new String[]{name,price};
	}
}
